package com.panopset.swing;

import static com.panopset.Util.getCanonicalPath;
import static com.panopset.Util.x;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
import java.util.Map.Entry;

import com.panopset.Util;
import com.panopset.UtilIO;
import com.panopset.io.LockedWriter;

/**
 * Document to GUI Scaffolding.
 *
 * @author Karl Dinwiddie.
 *
 */
public final class Scaffolding {
	/**
	 * Set this to true to turn off write safety feature.
	 */
	public static boolean trusted = false;

    /**
     * Scaffolding.
     */
    public static Scaffolding s = new Scaffolding();

    /**
     * @param applicationFrame
     *            App frame.
     */
    void doNew() {
        userChooseAppDirectory();
        flush();
    }

    /**
     * @param applicationFrame
     *            App frame.
     */
    void doOpen() {
        flush();
        userChooseAppDirectory();
        load();
    }

    /**
     * @param applicationFrame
     *            App frame.
     */
    void doSave() {
        flush();
    }

    /**
     * @param applicationFrame
     *            App frame.
     */
    void doSaveAs() {
        flush();
        userChooseAppDirectory();
        flush();
    }

    void reset() {
    	Field.reset2dft();
    }
    /**
     * Get a value from the current map document.
     *
     * @param key
     *            Key.
     * @return Value.
     */
    public String get(final String key) {
        return map.get(key);
    }

    private void userChooseAppDirectory() {
        File newFile = com.panopset.UtilIO.getDirectoryFromUser(getHome(),
                x("Please select application directory"));
        try {
			setHome(newFile);
		} catch (IOException e) {
			Util.log(e);
		}
    }

    /**
     * Put a value to the current map document.
     *
     * @param key
     *            Key.
     * @param value
     *            Value.
     */
    public void put(final String key, final String value) {
        map.put(key, value);
    }

    private final Map<String, String> map = new HashMap<String, String>();

    private File dir;
    
    private String appName;
    
    public String getAppName() {
    	if (appName == null) { 
        	if (App.app == null) {
                appName = "Scaffolding";
        	} else {
                appName = App.app.getApplicationName();
        	}
    	}
    	return appName;
    }
    
    /**
     * @param name Application name.
     */
    public void setAppName(final String name) {
    	if (appName != null) {
    		throw new RuntimeException("Logic error.");
    	}
    	appName = name;
    }
    
    private File h;
    
    /**
     * @param directory Directory.
     * @throws IOException If directory can not be used as a home directory.
     */
    public void setHome(final File directory) throws IOException {
        if (isSafeAppDirectory(directory)) {
            dir = directory;
            if (App.app != null) {
                App.app.f.getFrame().setTitle(getCanonicalPath(dir));
            }
        } else {
        	throw new IOException("Can not use "
        			+ Util.getCanonicalPath(directory));
        }
    }

    /**
     * @return Application directory within the base directory.
     */
    public File getHome() {
        if (dir == null) {
        	String hd = UtilIO.getHome();
        	if (App.app == null) {
        		hd = UtilIO.combinePaths(hd, "Panopset");
        	}
            File probe = new File(UtilIO.combinePaths(hd, getAppName()));
            if (isSafeAppDirectory(probe)) {
                dir = probe;
                return dir;
            } else {
                probe = new File(UtilIO.combinePaths(hd, "." + getAppName()));
            }
            while (probe == null || !isSafeAppDirectory(probe)) {
                probe = com.panopset.UtilIO.getDirectoryFromUser(probe,
                        "Please select application base directory");
                if (probe != null && !probe.exists()) {
                    if (!probe.mkdirs()) {
                        throw new RuntimeException(
                                "Could not create directory "
                                + getCanonicalPath(probe));
                    }
                }
                if (probe != null && probe.exists() && probe.isDirectory()) {
                    // user selected, okay to flag
                    flagDirectory(probe);
                    dir = probe;
                    return dir;
                }
            }
        }
        return dir;
    }

    private String getSimpleFlagName() {
        StringWriter sw = new StringWriter();
        
        sw.append(getAppName());
        sw.append("_READ_ME.txt");
        return sw.toString();
    }

    /**
     * Get flag.
     * @param dir
     * @return
     */
    private File getFlag(final File dir) {
        return new File(UtilIO.combinePaths(
                com.panopset.Util.getCanonicalPath(dir),
                getSimpleFlagName()));
    }

    private void flagDirectory(File dir) {
        File flag = getFlag(dir);
        if (!flag.exists()) {
            LockedWriter lw = new LockedWriter(flag);
            lw.write("This file resides in an application directory for ");
            lw.write(getAppName());
            lw.writeln(".");
            lw.writeln("");
            lw.writeln("Last updated:");
            lw.writeln(com.panopset.Util.getCurrentDateTime());
            lw.close();
        }
    }

    private final boolean isSafeAppDirectory(final File dir) {
        if (dir == null) {
            return false;
        }
        if (dir.exists() && dir.isFile()) {
            return false;
        }
        if (dir.exists()) {
            if (dir.isFile()) {
                return false;
            } else {
            	if (trusted) {
            		return true;
            	}
                if (getFlag(dir).exists()) {
                    return true;
                } else {
                    return false;
                }
            }
        } else {
            dir.mkdirs();
            flagDirectory(dir);
            return true;
        }
    }

    /**
     * @return app.txt.
     */
    private File createProps() {
        return new File(UtilIO.combinePaths(getCanonicalPath(getHome()),
                "app.txt"));
    }

    /**
     * Load all data.
     */
    public void load() {
        for (Entry<String, String> e : com.panopset.UtilIO.loadMap(
                createProps()).entrySet()) {
            map.put(e.getKey(), e.getValue());
        }
    }

    /**
     * Save all data.
     */
    public void flush() {
    	for (Field field : Field.fields) {
    		field.save();
    	}
        com.panopset.UtilIO.saveMap(map, createProps());
    }
    
    /**
     * @param rp Relative path.
     * @return File contents.
     */
    public static final Vector<String> loadFileFromAppDir(final String rp) {
    	return UtilIO.loadFile(new File(UtilIO.combinePaths(s.getHome(),
                rp)));
    }

}
